﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Xml;
using System.Xml.Linq;
using System.Xml.Schema;
using CommonBehaviors.Actions;
using Styx;
using Styx.CommonBot;
using Styx.TreeSharp;
using Styx.WoWInternals;
using Styx.WoWInternals.WoWObjects;

using Action = Styx.TreeSharp.Action;

namespace PetBuddy.Helpers
{
    public class Stabler
    {
        #region Variables

        private static LocalPlayer Me = PetBuddy.Me;

        public static bool HaveStableList;
        static string stablePath = Path.Combine(BotManager.BotsDirectory, BotManager.Current.Name, "Data\\StableList.xml");

        public static List<StableMasterStruct> MasterList = new List<StableMasterStruct>();

        #endregion

        #region Structs

        public struct StableMasterStruct
        {
            public int ID;
            public string Name;
            public int ContinentID;
            public WoWPoint Location;
        }

        #endregion

        #region XSD Validator

        const string XSD = @"<?xml version='1.0' encoding='UTF-8'?>
            <xs:schema xmlns:xs='http://www.w3.org/2001/XMLSchema' elementFormDefault='qualified'>
                <xs:element name='StableMasters_List'>
                <xs:complexType>
                    <xs:sequence>
                    <xs:element ref='Kalimdor'/>
                    <xs:element ref='EKingdoms'/>
                    <xs:element ref='Outland'/>
                    <xs:element ref='Northland'/>
                    <xs:element ref='Pandaria'/>
                    </xs:sequence>
                </xs:complexType>
                </xs:element>
                <xs:element name='Kalimdor'>
                <xs:complexType>
                    <xs:sequence>
                    <xs:element ref='A'/>
                    <xs:element ref='H'/>
                    <xs:element ref='B'/>
                    </xs:sequence>
                </xs:complexType>
                </xs:element>
                <xs:element name='EKingdoms'>
                <xs:complexType>
                    <xs:sequence>
                    <xs:element ref='A'/>
                    <xs:element ref='H'/>
                    <xs:element ref='B'/>
                    </xs:sequence>
                </xs:complexType>
                </xs:element>
                <xs:element name='Outland'>
                <xs:complexType>
                    <xs:sequence>
                    <xs:element ref='A'/>
                    <xs:element ref='H'/>
                    <xs:element ref='B'/>
                    </xs:sequence>
                </xs:complexType>
                </xs:element>
                <xs:element name='Northland'>
                <xs:complexType>
                    <xs:sequence>
                    <xs:element ref='A'/>
                    <xs:element ref='H'/>
                    <xs:element ref='B'/>
                    </xs:sequence>
                </xs:complexType>
                </xs:element>
                <xs:element name='Pandaria'>
                <xs:complexType>
                    <xs:sequence>
                    <xs:element ref='A'/>
                    <xs:element ref='H'/>
                    <xs:element ref='B'/>
                    </xs:sequence>
                </xs:complexType>
                </xs:element>
                <xs:element name='A'>
                <xs:complexType>
                    <xs:sequence>
                    <xs:element maxOccurs='unbounded' minOccurs='0' ref='Vendor'/>
                    <xs:element maxOccurs='unbounded' minOccurs='0' ref='Master'/>
                    </xs:sequence>
                </xs:complexType>
                </xs:element>
                <xs:element name='H'>
                <xs:complexType>
                    <xs:sequence>
                    <xs:element maxOccurs='unbounded' minOccurs='0' ref='Vendor'/>
                    <xs:element maxOccurs='unbounded' minOccurs='0' ref='Master'/>
                    </xs:sequence>
                </xs:complexType>
                </xs:element>
                <xs:element name='B'>
                <xs:complexType>
                    <xs:sequence>
                    <xs:element maxOccurs='unbounded' minOccurs='0' ref='Vendor'/>
                    <xs:element maxOccurs='unbounded' minOccurs='0' ref='Master'/>
                    </xs:sequence>
                </xs:complexType>
                </xs:element>
                <xs:element name='Vendor'>
                <xs:complexType>
                    <xs:attribute name='Entry' use='required' type='xs:integer'/>
                    <xs:attribute name='Name' use='required' type='xs:string'/>
                    <xs:attribute name='Type' use='required' type='xs:string'/>
                    <xs:attribute name='X' use='required' type='xs:decimal'/>
                    <xs:attribute name='Y' use='required' type='xs:decimal'/>
                    <xs:attribute name='Z' use='required' type='xs:decimal'/>
                </xs:complexType>
                </xs:element>
                <xs:element name='Master'>
                <xs:complexType>
                    <xs:attribute name='Entry' use='required' type='xs:integer'/>
                    <xs:attribute name='Name' use='required' type='xs:string'/>
                    <xs:attribute name='Type' use='required' type='xs:string'/>
                    <xs:attribute name='X' use='required' type='xs:decimal'/>
                    <xs:attribute name='Y' use='required' type='xs:decimal'/>
                    <xs:attribute name='Z' use='required' type='xs:decimal'/>
                </xs:complexType>
                </xs:element>
            </xs:schema>";

        static bool XSDValidator(string docPath)
        {
            var doc = XDocument.Load(docPath);
            var schemas = new XmlSchemaSet();
            schemas.Add("", XmlReader.Create(new StringReader(XSD)));

            var errors = false;
            doc.Validate(schemas, (o, e) =>
            {
                PetBuddy.rlog("StableList have not valid format \n{0}", e.Message);
                errors = true;
            });
            return !errors;
        }

        #endregion

        #region Utils

        static int ContinentNameToID(string name)
        {
            switch (name)
            {
                case "EKingdoms":
                    return 0;
                case "Kalimdor":
                    return 1;
                case "Outland":
                    return 530;
                case "Northland":
                    return 571;
                case "Pandaria":
                    return 870;
                default:
                    return -1;
            }
        }

        #endregion

        public static void GenerateStableList() 
        {
            if (!File.Exists(stablePath) & !XSDValidator(stablePath)) { HaveStableList = false; return; }

            PetBuddy.dlog("[Stabler] Generate StableMasters list");
            HaveStableList = false;

            try {
                var _list = XDocument.Load(stablePath);
                
                MasterList = (from XElement node in _list.Descendants("StableMasters_List").DescendantNodes()
                              where node.Name == "Vendor"
                              where (Me.IsAlliance && (node.Parent.Name == "A" || node.Parent.Name == "B") ||
                                   Me.IsHorde && (node.Parent.Name == "H" || node.Parent.Name == "B"))
                              select new StableMasterStruct
                              {
                                  ID = Convert.ToInt32(node.Attribute("Entry").Value),
                                  Name = node.Attribute("Name").Value,
                                  ContinentID = ContinentNameToID(node.Parent.Parent.Name.ToString()),

                                  Location = new WoWPoint
                                  {
                                      X = Single.Parse(node.Attribute("X").Value.Replace('.', ',')),
                                      Y = Single.Parse(node.Attribute("Y").Value.Replace('.', ',')),
                                      Z = Single.Parse(node.Attribute("Z").Value.Replace('.', ','))
                                  }
                              }
                             ).ToList();

            } catch (Exception er) {
                PetBuddy.dlog("Error parsing StableList \n{0}", er.Message);
                HaveStableList = false;
                return;
            }
            HaveStableList = true;
        }

        public static StableMasterStruct NearestStableMaster
        {
            get
            {
                if (!HaveStableList) return new StableMasterStruct();

                var _stable = (from master in MasterList
                        where master.ContinentID == Me.MapId
                        orderby master.Location.Distance(Me.Location) ascending
                        select master).FirstOrDefault();

                PetBuddy.dlog("[Stabler] Nearest stable Master {0} at {1} on Continet with ID {2}({3}), distance is {4}", 
                    _stable.Name, _stable.Location.ToString(), _stable.ContinentID, Me.MapId, _stable.Location.Distance(Me.Location));

                return _stable;
            }
        }
        
        public static Composite UseNearestStable()
        {
            return new PrioritySelector(

                new Decorator(ret => NearestStableMaster.ID <= 0,
                              new Action(ret =>
                                  {
                                      HaveStableList = false;
                                      return RunStatus.Success;
                                  })),

                new Decorator(ret => NearestStableMaster.Location.Distance(Me.Location) > 5f,
                              new Sequence(
                                  PetBuddy.adlog("[Stabler] Moving to Master {0} at {1}", NearestStableMaster.Name, NearestStableMaster.Location.ToString()),
                                  new Action(ret => Mover.navSelector = Mover.NavSelector.STABLE),
                                  Mover.MoveToTargetNavPoint(),
                                  new ActionAlwaysFail()
                                  )),

                new Decorator(ret => NearestStableMaster.Location.Distance(Me.Location) <= 5f,
                    InteractWithStableMaster()
                    )
                );
        }

        static Composite InteractWithStableMaster()
        {
            return new PrioritySelector(
                
                new ActionAlwaysFail()
                );
        }
    }
}
